/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.explorer.ui.custom;

import com.vaadin.ui.Upload;
import com.vaadin.ui.Upload.ProgressListener;
import com.vaadin.ui.Upload.Receiver;
import com.vaadin.ui.Upload.StartedEvent;
import com.vaadin.ui.Upload.StartedListener;
import org.activiti.explorer.ExplorerApp;
import org.activiti.explorer.Messages;
import org.activiti.explorer.NotificationManager;
import org.activiti.explorer.util.StringUtil;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Collection;


/**
 * Generic {@link Receiver} for an {@link Upload} component.
 * Stores the bytes in-memory, so be careful to limit the size of the uploads.
 *
 * @author Joram Barrez
 */
public class InMemoryUploadReceiver implements Receiver, StartedListener, ProgressListener {

    private static final long serialVersionUID = 1L;
    protected NotificationManager notificationManager;
    protected Upload upload;
    protected boolean interrupted;
    protected String fileName;
    protected String mimeType;
    protected long maxFileSize;
    protected ByteArrayOutputStream outputStream;
    protected Collection<String> acceptedMimeTypes;

    /**
     * @param upload      The component that will serve this receiver
     * @param maxFileSize The maximum size of files that will be accepted (in bytes). -1 in case of no limit.
     */
    public InMemoryUploadReceiver(Upload upload, long maxFileSize) {
        this.upload = upload;
        this.maxFileSize = maxFileSize;
        this.notificationManager = ExplorerApp.get().getNotificationManager();

        upload.setReceiver(this);
        upload.addListener((StartedListener) this);
        upload.addListener((ProgressListener) this);
    }

    public OutputStream receiveUpload(String filename, String mimeType) {
        this.fileName = filename;
        this.mimeType = mimeType;
        this.outputStream = new ByteArrayOutputStream();
        return outputStream;
    }

    public void uploadStarted(StartedEvent event) {
        checkFileSize(event.getContentLength());
        if (!interrupted) { // upload can be interrupted by invalid file size
            checkMimeType(event.getMIMEType());
        }
    }

    public void updateProgress(long readBytes, long contentLength) {
        if (contentLength == -1) { // Older browsers don't support getting the filesize
            checkFileSize(readBytes);
        } else {
            checkFileSize(contentLength);
        }
    }

    public byte[] getBytes() {
        return outputStream.toByteArray();
    }

    public String getFileName() {
        return fileName;
    }

    public String getMimeType() {
        return mimeType;
    }

    public boolean isInterruped() {
        return interrupted;
    }

    protected void checkFileSize(long receivedLength) {
        if (receivedLength > maxFileSize) {
            interrupt();
            notificationManager.showWarningNotification(Messages.UPLOAD_FAILED,
                    Messages.UPLOAD_LIMIT, maxFileSize / 1024 + "kb");
        }
    }

    protected void checkMimeType(String mimeType) {
        if (acceptedMimeTypes != null && !acceptedMimeTypes.contains(mimeType)) {
            interrupt();
            notificationManager.showWarningNotification(Messages.UPLOAD_FAILED,
                    Messages.UPLOAD_INVALID_MIMETYPE,
                    StringUtil.toReadableString(acceptedMimeTypes));
        }
    }

    protected void interrupt() {
        upload.interruptUpload();
        interrupted = true;
    }

    /**
     * By default, all mime types are accepted.
     * By providing a set of mimetypes, the receiver will only work with the given mimetypes
     */
    public void setAcceptedMimeTypes(Collection<String> acceptedMimeTypes) {
        this.acceptedMimeTypes = acceptedMimeTypes;
    }

    public void reset() {
        interrupted = false;
        outputStream = null;
        fileName = null;
        mimeType = null;
    }

}
